package com.sh.data.engine.domain.normalization.modeldesign.busmatrix.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sh.data.engine.common.constants.HttpStatus;
import com.sh.data.engine.common.constants.code.BizCodes;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.model.domain.BusMatrixDomain;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.model.domain.LogicRelFieldDomain;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.model.mapper.RelFieldMapper;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.model.param.SaveBizRelParam;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.model.param.SaveLogicRelParam;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.service.BusMatrixFieldService;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.service.BusMatrixService;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.service.DimFactService;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.ModelDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.ModelIdNameDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.service.ModelService;
import com.sh.data.engine.domain.normalization.research.themedesign.model.domain.ThemeDomain;
import com.sh.data.engine.repository.dao.dataasset.entity.TblPropEntity;
import com.sh.data.engine.repository.dao.normalization.modeldesign.busmatrix.BusMatrixMapper;
import com.sh.data.engine.repository.dao.normalization.modeldesign.busmatrix.arg.SaveLogicRelArg;
import com.sh.data.engine.repository.dao.normalization.modeldesign.busmatrix.entity.MatrixEntity;
import com.sh.data.engine.repository.dao.normalization.modeldesign.dimfact.entity.DimFactEntity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author : changkong
 * @date : 2022/1/10
 */
@Slf4j
@Service
public class BusMatrixServiceImpl extends ServiceImpl<BusMatrixMapper, MatrixEntity>
    implements BusMatrixService {

    @Autowired
    private BusMatrixFieldService busMatrixFieldService;

    @Autowired
    private DimFactService dimFactService;

    @Lazy
    @Autowired
    private ModelService modelService;

    @Autowired
    private RelFieldMapper relFieldMapper;

    /**
     * 获取左侧主题列表
     *
     * @param projectId
     * @return
     */
    @Override
    public List<ThemeDomain> getThemesByProjectId(Long projectId, String modelType) {
        List<ThemeDomain> themeDomains = modelService.getThemesByProjectId(projectId, modelType);
        return themeDomains;
    }

    /**
     * 新增行或者列, 更新matrix
     *
     * @param sourceMatrix  数据库中已经存在的矩阵元素集合
     * @param rowSortedList 已经排序过的行
     * @param colSortedList 已经排序过的列
     * @param insertEntity  新增的行
     * @return
     */
    @Override
    public BusMatrixDomain rebuildMatrix(
        List<MatrixEntity> sourceMatrix,
        List<DimFactEntity> rowSortedList,
        List<DimFactEntity> colSortedList,
        DimFactEntity insertEntity) {
        // 只要有一个list为空，无法计算matrix，直接返回
        if (CollectionUtils.isEmpty(rowSortedList) || CollectionUtils.isEmpty(colSortedList)) {
            return buildEmptyMatrix(rowSortedList, colSortedList);
        }
        // 直接组装由新建行或者列构建出的节点列表
        List<MatrixEntity> newMatrixEntityList =
            buildIncrementalDbMatrix(rowSortedList, colSortedList, insertEntity);
        // 批量插入新建的节点列表
        this.saveBatch(newMatrixEntityList);
        // merge原矩阵与增量矩阵
        boolean mergeResult = sourceMatrix.addAll(newMatrixEntityList);
        if (!mergeResult) {
            log.error("增量矩阵合并失败");
            throw new IllegalStateException("状态异常");
        }
        // 构建展示数据并返回
        return buildApiMatrix(rowSortedList, colSortedList, sourceMatrix);
    }

    /**
     * 根据主题下的factIds 和 dimIds 筛选出矩阵节点
     *
     * @param themeId
     * @return
     */
    @Override
    public BusMatrixDomain getBusMatrixByTheme(Long themeId) {
        // 拉取全量factEntity
        Pair<List<DimFactEntity>, List<DimFactEntity>> pair =
            dimFactService.fullFetchDimFactsAndPartByType(themeId);
        List<DimFactEntity> factLists = pair.getFirst();
        List<DimFactEntity> dimLists = pair.getSecond();
        // 查factId和dimId对应的matrix
        List<Long> factIds = factLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        List<Long> dimIds = dimLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        List<MatrixEntity> sourceMatrix = getBusMatrixEntityByFactIdsAndDimIds(factIds, dimIds);
        // 构建展示数据并返回
        return buildApiMatrix(factLists, dimLists, sourceMatrix);
    }

    @Override
    public List<MatrixEntity> getBusMatrixEntityByFactIdsAndDimIds(
        List<Long> factId, List<Long> dimId) {
        List<MatrixEntity> busMatrixByTheme =
            this.list(
                new LambdaQueryWrapper<MatrixEntity>()
                    .in(CollectionUtils.isNotEmpty(factId), MatrixEntity::getFactId, factId)
                    .in(CollectionUtils.isNotEmpty(dimId), MatrixEntity::getDimId, dimId)
                    .eq(MatrixEntity::getRowState, 1));
        if (null == busMatrixByTheme) {
            log.error("fact:{}, dim:{} matrix表id查找失败 ", factId, dimId);
            throw new IllegalStateException("状态异常");
        }
        return busMatrixByTheme;
    }

    /**
     * 在matrix表中找到rowId, colId对应的节点
     *
     * @param saveBizRelParam
     */
    @Override
    public void saveBizRelRequest(SaveBizRelParam saveBizRelParam) {
        Long factIdx = saveBizRelParam.getFactRowId();
        Long dimIdx = saveBizRelParam.getDimColId();
        Integer bizRel = saveBizRelParam.getBizRel();
        this.update(
            new LambdaUpdateWrapper<MatrixEntity>()
                .eq(MatrixEntity::getFactId, factIdx)
                .eq(MatrixEntity::getDimId, dimIdx)
                .set(MatrixEntity::getBizRel, bizRel));
    }

    /**
     * 新增行|列 产生了增量部分的矩阵节点,
     *
     * @param factRows
     * @param dimCols
     * @return
     */
    private List<MatrixEntity> buildIncrementalDbMatrix(
        List<DimFactEntity> factRows, List<DimFactEntity> dimCols, DimFactEntity insertEntity) {
        Long projectId = ServletUtils.getProjectId();
        List<MatrixEntity> newEntityList = new ArrayList<>();
        // 新增的是行,构造与列的交集产生的矩阵节点
        if ("fact".equals(insertEntity.getType())) {
            // 遍历dimCols
            for (DimFactEntity item : dimCols) {
                MatrixEntity newEntity = new MatrixEntity();
                newEntity.setProjectId(projectId);
                newEntity.setFactId(insertEntity.getId());
                newEntity.setDimId(item.getId());
                // 创建出来的matrix，没有业务和逻辑相关
                //        newEntity.setFactField(null);
                //        newEntity.setDimField(null);
                newEntity.setLogicRel(0);
                newEntity.setBizRel(0);
                newEntity.setRowState(1);
                newEntityList.add(newEntity);
            }
        } else if ("dim".equals(insertEntity.getType())) {
            // 遍历factRows
            for (DimFactEntity item : factRows) {
                MatrixEntity newEntity = new MatrixEntity();
                newEntity.setProjectId(projectId);
                newEntity.setFactId(item.getId());
                newEntity.setDimId(insertEntity.getId());
                // 创建出来的matrix，没有业务和逻辑相关
                //        newEntity.setFactField(null);
                //        newEntity.setDimField(null);
                newEntity.setLogicRel(0);
                newEntity.setBizRel(0);
                newEntity.setRowState(1);
                newEntityList.add(newEntity);
            }
        } else {
            log.error("新增行|列类型错误 id {} {}", insertEntity.getId(), insertEntity.getType());
            throw new IllegalStateException("状态异常");
        }
        return newEntityList;
    }

    /**
     * 按照给定的带顺序的行列构建出接口返回值
     *
     * @param factRows
     * @param dimCols
     * @return
     */
    @Override
    public BusMatrixDomain buildApiMatrix(
        List<DimFactEntity> factRows, List<DimFactEntity> dimCols, List<MatrixEntity> sourceMatrix) {
        List<List<BusMatrixDomain.RelItem>> matrixResults = new ArrayList<>();
        Map<DimFactKey, MatrixEntity> uniqueIdEntityMap =
            sourceMatrix.stream()
                .collect(
                    Collectors.toMap(
                        (i) -> DimFactKey.builder().factId(i.getFactId()).dimId(i.getDimId()).build(),
                        Function.identity()));
        BusMatrixDomain busMatrixDomain = buildEmptyMatrix(factRows, dimCols);
        // 无法构建矩阵
        if (CollectionUtils.isEmpty(factRows) || CollectionUtils.isEmpty(dimCols)) {
            return busMatrixDomain;
        }
        for (DimFactEntity factRow : factRows) {
            List<BusMatrixDomain.RelItem> rowItems = new ArrayList<>();
            Long factId = factRow.getId();
            for (DimFactEntity dimCol : dimCols) {
                BusMatrixDomain.RelItem relItem = new BusMatrixDomain.RelItem();
                Long dimId = dimCol.getId();
                // 构建矩阵domain
                MatrixEntity matrixEntity =
                    uniqueIdEntityMap.get(DimFactKey.builder().factId(factId).dimId(dimId).build());
                if (null == matrixEntity) {
                    log.error("矩阵构建异常 {} {}", factId, dimId);
                    throw new IllegalStateException("状态异常");
                }
                relItem.setLogicRel(matrixEntity.getLogicRel());
                relItem.setBizRel(matrixEntity.getBizRel());
                rowItems.add(relItem);
            }
            matrixResults.add(rowItems);
        }
        busMatrixDomain.setRelItemList(matrixResults);
        return busMatrixDomain;
    }

    /**
     * 通过model来获取table信息
     *
     * @param factRows
     * @param dimCols
     * @return
     */
    @NotNull
    private Map<Long, TblPropEntity> getModelIdTblPropEntityMap(
        List<DimFactEntity> factRows, List<DimFactEntity> dimCols) {
        List<Long> fullModelIdList =
            factRows.stream()
                .map(DimFactEntity::getTblPropId)
                .filter(i -> !Objects.isNull(i))
                .collect(Collectors.toList());
        fullModelIdList.addAll(
            dimCols.stream()
                .map(DimFactEntity::getTblPropId)
                .filter(i -> !Objects.isNull(i))
                .collect(Collectors.toList()));
        // 获取所有的模型信息
        log.info("$in 模型集合数量: {}", fullModelIdList.size());
        Map<Long, TblPropEntity> tblIdMap = modelService.getModelIdTblMap(fullModelIdList);
        return tblIdMap;
    }

    /**
     * 构建空矩阵
     *
     * @param factRows
     * @param dimCols
     * @return
     */
    private BusMatrixDomain buildEmptyMatrix(
        List<DimFactEntity> factRows, List<DimFactEntity> dimCols) {
        BusMatrixDomain busMatrixDomain = new BusMatrixDomain();
        List<BusMatrixDomain.FactRow> resultFacts = new ArrayList<>();
        List<BusMatrixDomain.DimCol> resultDims = new ArrayList<>();
        Map<Long, TblPropEntity> tblIdMap = getModelIdTblPropEntityMap(factRows, dimCols);
        for (DimFactEntity factRow : factRows) {
            Long factId = factRow.getId();
            // 构建事实行domain
            BusMatrixDomain.FactRow resultFactItem = new BusMatrixDomain.FactRow();
            resultFactItem.setId(factId);
            resultFactItem.setName(factRow.getName());
            resultFactItem.setDesc(factRow.getDescribe());
            Long modelId = factRow.getTblPropId();
            // 是否绑定模型
            boolean isRefTable = null != modelId;
            resultFactItem.setRefModel(isRefTable);
            TblPropEntity table = tblIdMap.get(modelId);
            resultFactItem.setFactModelId((isRefTable && (null != table)) ? modelId : null);
            resultFacts.add(resultFactItem);
        }
        for (DimFactEntity dimCol : dimCols) {
            Long dimId = dimCol.getId();
            // 构建事实行domain
            BusMatrixDomain.DimCol resultDimItem = new BusMatrixDomain.DimCol();
            resultDimItem.setId(dimId);
            resultDimItem.setName(dimCol.getName());
            Long dimTableId = dimCol.getTblPropId();
            boolean isDimRefTable = null != dimTableId;
            // 是否绑定表
            TblPropEntity dimTable = tblIdMap.get(dimTableId);
            resultDimItem.setRefModel(isDimRefTable);
            resultDimItem.setDimModelId((isDimRefTable && (null != dimTable)) ? dimTableId : null);
            resultDims.add(resultDimItem);
        }
        // 这里还需要进行一下转换
        busMatrixDomain.setFactRowList(resultFacts);
        busMatrixDomain.setDimColList(resultDims);
        return busMatrixDomain;
    }

    /**
     * 该行或者列是否绑定了逻辑关联
     *
     * @param type
     * @param id
     * @return
     */
    @Override
    public Boolean isRowColBindField(String type, Long id) {
        List<MatrixEntity> result =
            this.list(
                new LambdaQueryWrapper<MatrixEntity>()
                    .eq(
                        Objects.equals(type, "dim") ? MatrixEntity::getDimId : MatrixEntity::getFactId,
                        id)
                    .eq(MatrixEntity::getRowState, 1)
                    .eq(MatrixEntity::getLogicRel, 1));
        return CollectionUtils.isNotEmpty(result);
    }

    /**
     * 查询主题下, 多种类型的模型表
     *
     * @param themeId
     * @param type
     * @return
     */
    @Override
    public List<ModelIdNameDomain> getModelListByType(Long themeId, String type) {
        return modelService.getModelListByType(themeId, type);
    }

    /**
     * 查找已关联的 字段对 集合
     *
     * @param factRowId
     * @param dimColId
     * @return
     */
    @Override
    public List<LogicRelFieldDomain> getLogicRelFieldList(Long factRowId, Long dimColId) {
        MatrixEntity matrixEntity = getMatrixByFactAndDim(factRowId, dimColId);
        if (null == matrixEntity) {
            return new ArrayList<>();
        }
        return busMatrixFieldService.getLogicRelFields(matrixEntity.getId());
    }

    /**
     * 设置逻辑关连
     *
     * @param saveLogicRelParam
     */
    @Override
    public void saveLogicRel(SaveLogicRelParam saveLogicRelParam) {
        Long factRowId = saveLogicRelParam.getFactRowId();
        Long dimColId = saveLogicRelParam.getDimColId();
        List<SaveLogicRelParam.RelField> requestRelFieldList = saveLogicRelParam.getRelFields();
        List<LogicRelFieldDomain> dbLogicRelFieldList = getLogicRelFieldList(factRowId, dimColId);
        if (CollectionUtils.isEmpty(requestRelFieldList)) {
            if (CollectionUtils.isEmpty(dbLogicRelFieldList)) {
                log.info("factId: {}, dimId: {} 原关联字段列表为空,请求体中关联字段列表为空,不操作", factRowId, dimColId);
                // 不处理
                return;
            }
            // 原先有字段关联,但是现在取消了所有字段关联,设置logicRel为false的同时设置bizRel为false
            SaveLogicRelArg saveLogicRelArg =
                buildSaveLogicRelArg(factRowId, dimColId, 0, 0, new ArrayList<>());

            Boolean updateResult = this.cancelMatrixLogicRel(saveLogicRelArg);
            if (!updateResult) {
                log.error(
                    "取消逻辑关联异常, factId:{} dimId:{} fields:{}", factRowId, dimColId, requestRelFieldList);
                throw new BusinessException(BizCodes.CANCEL_MATRIX_LOGICAL_REL_ERROR, HttpStatus.BAD_REQUEST);
            }
            log.info("取消factId: {}, dimId: {} 逻辑关联成功", factRowId, dimColId);
            return;
        }
        // 判断字段是否有差异
        boolean diffFieldsFlag = false;
        if (requestRelFieldList.size() == dbLogicRelFieldList.size()) {
            // 按字典序排序
            List<SaveLogicRelParam.RelField> sortedRequestFieldList =
                requestRelFieldList.stream()
                    .sorted(Comparator.comparing(SaveLogicRelParam.RelField::getFactField))
                    .collect(Collectors.toList());
            List<LogicRelFieldDomain> sortedDbLogicRelFieldList =
                dbLogicRelFieldList.stream()
                    .sorted(Comparator.comparing(LogicRelFieldDomain::getFactField))
                    .collect(Collectors.toList());
            // 比较排序后的list
            for (int i = 0; i < sortedRequestFieldList.size(); i++) {
                SaveLogicRelParam.RelField requestItem = sortedRequestFieldList.get(i);
                LogicRelFieldDomain dbItem = sortedDbLogicRelFieldList.get(i);
                if (Objects.isNull(requestItem)
                    || Objects.isNull(dbItem)
                    || !Objects.equals(requestItem.getDimField(), dbItem.getDimField())
                    || !Objects.equals(requestItem.getFactField(), dbItem.getFactField())) {
                    diffFieldsFlag = true;
                    break;
                }
            }
        } else {
            // 数量不一致需要重新设置
            diffFieldsFlag = true;
        }
        // 没有差异直接返回
        if (!diffFieldsFlag) {
            log.info("字段列表仅调整了顺序,不进行保存操作");
            return;
        }
        // 更新关联数组
        List<SaveLogicRelArg.RelField> fields = relFieldMapper.map(requestRelFieldList);
        SaveLogicRelArg saveLogicRelArg = buildSaveLogicRelArg(factRowId, dimColId, 1, 1, fields);
        Boolean updateResult = this.saveMatrixLogicRel(saveLogicRelArg);
        if (!updateResult) {
            log.error("设置逻辑关联异常, factId:{} dimId:{} fields:{}", factRowId, dimColId, requestRelFieldList);
            throw new IllegalStateException();
        }
        log.info("更新factId: {}, dimId: {} 逻辑关联成功", factRowId, dimColId);
    }

    /**
     * 获取事实表与维度表的所有字段
     *
     * @param projectId
     * @param factId
     * @param dimId
     * @return
     */
    @Override
    public Pair<List<ModelDomain.Field>, List<ModelDomain.Field>> getDimFieldFactFieldList(
        Long projectId, Long factId, Long dimId) {
        ModelDomain factModel = modelService.getModelById(projectId, factId);
        List<ModelDomain.Field> factIds = factModel.getFields();
        ModelDomain dimModel = modelService.getModelById(projectId, dimId);
        List<ModelDomain.Field> dimIds = dimModel.getFields();
        return Pair.of(factIds, dimIds);
    }

    @NotNull
    private SaveLogicRelArg buildSaveLogicRelArg(
        Long factRowId,
        Long dimColId,
        Integer logicRel,
        Integer bizRel,
        List<SaveLogicRelArg.RelField> list) {
        SaveLogicRelArg saveLogicRelArg = new SaveLogicRelArg();
        saveLogicRelArg.setFactId(factRowId);
        saveLogicRelArg.setDimId(dimColId);
        saveLogicRelArg.setLogicRelValue(logicRel);
        saveLogicRelArg.setBizRelValue(bizRel);
        saveLogicRelArg.setRelFields(list);
        return saveLogicRelArg;
    }

    /**
     * 删除矩阵的行或者列
     *
     * @param id
     * @param type {dim | fact}
     */
    @Override
    public void deleteMatrixItems(Long id, String type) {
        this.update(
            new LambdaUpdateWrapper<MatrixEntity>()
                .eq(Objects.equals(type, "dim") ? MatrixEntity::getDimId : MatrixEntity::getFactId, id)
                .eq(MatrixEntity::getRowState, 1)
                .set(MatrixEntity::getRowState, 0));
    }

    /**
     * 获取行元素
     *
     * @param factId
     * @return
     */
    @Override
    public List<MatrixEntity> getMatrixByFactId(Long factId) {
        return this.list(
            new LambdaQueryWrapper<MatrixEntity>()
                .eq(MatrixEntity::getFactId, factId)
                .eq(MatrixEntity::getRowState, 1));
    }

    /**
     * 行列获取矩阵
     *
     * @param factRowId
     * @param dimColId
     * @return
     */
    private MatrixEntity getMatrixByFactAndDim(Long factRowId, Long dimColId) {
        MatrixEntity matrixEntity =
            this.getOne(
                new LambdaQueryWrapper<MatrixEntity>()
                    .eq(MatrixEntity::getFactId, factRowId)
                    .eq(MatrixEntity::getDimId, dimColId)
                    .eq(MatrixEntity::getRowState, 1));
        return matrixEntity;
    }

    /**
     * 同时取消矩阵中逻辑关联,业务关联状态,关联字段数组
     *
     * @param saveLogicRelArg
     */
    @Transactional(rollbackFor = Exception.class, transactionManager = "engineTransactionManager")
    public Boolean cancelMatrixLogicRel(SaveLogicRelArg saveLogicRelArg) {
        MatrixEntity matrixEntity =
            this.getMatrixByFactAndDim(saveLogicRelArg.getFactId(), saveLogicRelArg.getDimId());
        if (null == matrixEntity) {
            throw new BusinessException("未找到对应的矩阵节点");
        }
        // 取消关联
        this.update(
            new LambdaUpdateWrapper<MatrixEntity>()
                .eq(MatrixEntity::getId, matrixEntity.getId())
                .eq(MatrixEntity::getRowState, 1)
                .set(MatrixEntity::getLogicRel, 0)
                .set(MatrixEntity::getBizRel, 0));

        // 取消字段设置
        return busMatrixFieldService.cancelLogicRelFields(matrixEntity.getId());
    }

    /**
     * 同时取消矩阵中逻辑关联,业务关联状态,关联字段数组
     *
     * @param saveLogicRelArg
     */
    @Transactional(rollbackFor = Exception.class, transactionManager = "engineTransactionManager")
    public Boolean saveMatrixLogicRel(SaveLogicRelArg saveLogicRelArg) {
        MatrixEntity matrixEntity =
            this.getMatrixByFactAndDim(saveLogicRelArg.getFactId(), saveLogicRelArg.getDimId());
        if (null == matrixEntity) {
            throw new BusinessException("未找到对应的矩阵节点");
        }
        // matrix表设置关联
        this.update(
            new LambdaUpdateWrapper<MatrixEntity>()
                .eq(MatrixEntity::getId, matrixEntity.getId())
                .eq(MatrixEntity::getRowState, 1)
                .set(MatrixEntity::getLogicRel, 1)
                .set(MatrixEntity::getBizRel, 1));

        // 设置字段关联
        return busMatrixFieldService.saveLogicRelFields(
            matrixEntity.getId(), saveLogicRelArg.getRelFields());
    }

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class DimFactKey {
        private Long factId;
        private Long dimId;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            DimFactKey that = (DimFactKey) o;
            return Objects.equals(factId, that.factId) && Objects.equals(dimId, that.dimId);
        }

        @Override
        public int hashCode() {
            return Objects.hash(factId, dimId);
        }
    }
}
